<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title></title>
    <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <!-- <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap-theme.min.css" rel="stylesheet"> -->
    <style>
    </style>
</head>
<body class="container">
    <!-- <script src="https://unpkg.com/vue"></script> -->
    <!-- <script src="https://cdn.jsdelivr.net/npm/vue"></script> -->

    <script src="https://cdn.bootcss.com/vue/2.5.3/vue.js"></script>
    <!-- <script src="https://cdn.bootcss.com/vue/2.5.3/vue.min.js"></script> -->

    <!-- <script src="https://lodash.com/vendor/cdn.jsdelivr.net/lodash/4.17.4/lodash.min.js"></script> -->

    <!-- <script src="https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"></script> -->
    <!-- <script src="https://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script> -->

    <!-- 组件 -->

    <!-- 使用组件 -->

    <!-- 使用组件 - 全局注册 -->
    <div id="example">
        <my-component></my-component>
    </div>
    <script>
        Vue.component('my-component', {
            template: '<div>A custom component!</div>',
        });

        new Vue({
            el: '#example',
        });
    </script>
    <!-- 使用组件 - 局部注册 -->
    <div id="example1">
        <my-component1></my-component1>
    </div>
    <script>
        let child = {
            template: '<div>A custom component!</div>',
        };

        new Vue({
            el: '#example1',
            components: {
                'my-component1': child,
            },
        });
    </script>

    <!-- 使用组件 - DOM 模板解析注意事项 -->
    <!-- 当使用 DOM 作为模板时,会受到 HTML 本身的一些限制，因为 Vue 只在浏览器解析、
        规范化之后才能获取其内容。像 `<ul>`、`<ol>`、`table`、`select` 这样的元素里
        允许包含的元素有限制，而另一些像 <option> 这样的元素只能出现在某些特定元素
        的内部。 -->
    <!-- 变通方案是使用特殊的 is 特性 -->
    <!-- 如果使用以下字符串模板，则没有限制：
        * `<script type="text/x-template">`
        * JavaScript 内联模板字符串
        * `.vue` 组件
        因此，请尽可能使用字符串模板 -->
    <table id="tbl">
        <tr is="my-row"></tr>
    </table>
    <script>
        let rowTemplateObj = {
            template: '<td>A custom component!</td>',
        };

        new Vue({
            el: '#tbl',
            components: {
                'my-row': rowTemplateObj,
            },
        });
    </script>

    <!-- 使用组件 - data 必须是函数 -->
    <!-- 避免多个的组件共享一个对象 -->
    <div id="example2">
        <simple-counter></simple-counter>
        <simple-counter></simple-counter>
        <simple-counter></simple-counter>
    </div>
    <script>
        let data = {counter: 0};

        Vue.component('simple-counter', {
            template: '<button v-on:click="counter += 1">{{ counter }}</button>',
            // data: () => data, // X
            data: () => {
                return {counter: 0};
            },
        });

        new Vue({
            el: '#example2',
        });
    </script>

    <!-- 使用组件 - 组件组合 -->
    <!-- 父子组件的关系可以总结为 **prop 向下传递，事件向上传递**。 -->
    <!-- 父组件通过 prop 给子组件下发数据，子组件通过事件给父组件发送消息。 -->

    <!-- Prop -->

    <!-- Prop - 使用 Prop 传递数据 -->
    <!-- 组件实例的作用域是孤立的。这意味着不能在子组件的模板内直接引用父组件的数据。
        父组件的数据需要通过 prop 才能下发到子组件中。
        子组件哟啊显示地用 props 选项声明它预期的数据。-->

    <div id="prop-example-1">
        <child message="hello!"></child>
    </div>
    <script>
        /*Vue.component('child', {
            props: ['message'],
            template: '<span>{{ message }}</span>',
        });*/

        new Vue({
            el: '#prop-example-1',
            components: {
                child: {
                    props: ['message'],
                    template: '<span>{{ message }}</span>',
                },
            },
        });

    </script>

    <!-- Prop - 动态 Prop -->
    <!-- 可以用 v-bind 来动态地将 prop 绑定到父组件的数据。
        当父组件的数据变化时，该变化也会传导给子组件。 -->
    <div id="prop-example-2">
        <input v-model="parentMsg"><br>
        <child v-bind:my-message="parentMsg"></child>
    </div>
    <script>
        new Vue({
            el: '#prop-example-2',
            data: {
                parentMsg: 'Message from parent',
            },
            components: {
                child: {
                    props: ['myMessage'],
                    template: '<span>{{ myMessage }}</span>',
                }
            },
        });
    </script>

    <!-- Prop - 字面量语法 vs 动态语法 -->
    <!-- 使用 v-bind 绑定变量的值 -->

    <!-- Prop - 单向数据流 -->
    <!-- 当父组件的属性变化时，将传到给子组件，但是反过来不会。
        每次父组件更新时，子组件的所有 prop 都会更新为最新值。
        这意味着不应该在子组件内部改变 prop。 -->
    <!-- 在两种情况下，想修改 prop 中的数据：
        1. Prop 作为初始值传入后，子组件想把它当作局部数据来用；
        2. Prop 作为原始数据传入，由子组件处理成其它数据输出。
        正确的应对方式：
        1. 定义一个局部变量，并用 prop 的值初始化它；
        2. 定义一个计算属性，处理 prop 的值并返回。-->

    <!-- Prop - Prop 验证 -->
    <!-- 要指定验证规则，需要用对象的形式来定义 prop，而不能用字符串数据。 -->
    <script>
        Vue.component('example-type', {
            props: {
                propA: Number,
                propB: [String, Number],
                propC: {
                    type: String,
                    required: true,
                },
                propD: {
                    type: Number,
                    default: 100,
                },
                propE: {
                    type: Object,
                    default: () => {message: 'hello'},
                },
                propF: {
                    validator: value => value > 10,
                },
            },
        });
    </script>

    <!-- 非 Prop 特性 -->

    <!-- 非 Prop 特性 - 替换/合并现有的特性 -->
    <!-- 传递给组件的值不会覆盖组件本身设定的值，会进行合并操作 -->

    <!-- 自定义事件 -->

    <!-- 自定义事件 - 使用 v-on 绑定自定义事件 -->
    <!-- 父组件可以在使用子组件的地方直接用 v-on 监听子组件触发的事件，
        不能用 $on 侦听子组件释放的事件，而必须在模板里直接用 v-on 绑定。 -->
    <!-- 在本例中，子组件已经和它外部完全解耦了。它所做的只是报告自己的内部事件，
        因为父组件可能会关心这些事件。请注意这一点很重要。 -->
    <div id="counter-event-example">
        <p>{{ total }}</p>
        <button-counter v-on:increment="incrementTotal"></button-counter>
        <button-counter v-on:increment="incrementTotal"></button-counter>
    </div>
    <script>
        Vue.component('button-counter', {
            template: '<button v-on:click="incrementCoutner">{{ counter }}</button>',
            data() {
                return {counter: 0};
            },
            methods: {
                incrementCoutner() {
                    this.counter += 1;
                    this.$emit('increment');
                },
            },
        });

        new Vue({
            el: '#counter-event-example',
            data: {
                total: 0,
            },
            methods: {
                incrementTotal() {
                    this.total += 1;
                },
            },
        })
    </script>

    <!-- 自定义事件 - 给组件绑定原生事件 -->
    <!-- <my-component v-on:click.native="doThing"></my-component> -->

    <!-- 自定义事件 - .sync 修饰符 -->
    <!-- 让子组件改变父组件状态的代码更容易被区分。
        作为一个编译时的语法糖存在。
        它会被扩展为一个自动更新父组件属性的 v-on 监听器。 -->
    <!-- 如下代码 -->
    <!-- <comp :foo.sync="bar"></comp> -->
    <!-- 会被扩展为： -->
    <!-- <comp :foo="bar" @update:foo="val => bar = val"></comp> -->
    <!-- 当子组件需要更新 foo 的值时，它需要显示地触发一个更新事件： -->
    <!-- this.$emit('update:foo', newValue) -->

    <!-- 自定义事件 - 使用自定义事件的表单输入组件 -->
    <!-- 要让组件的 v-model 生效，应该：
        接受一个 `value` prop
        在有新的值时触发 `input` 事件并将新值作为参数 -->
    <div id="currency-input-example">
        <currency-input v-model="price"></currency-input>
    </div>
    <script>
        Vue.component('currency-input', {
            template: `
                <span>
                    $<input ref="input" v-bind:value="value"
                        v-on:input="updateValue($event.target.value)">
                </span>
            `,
            props: ['value'],
            methods: {
                updateValue(value) {
                    let formattedValue = value.trim().slice(0,
                        value.indexOf('.') === -1 ? value.length : value.indexOf('.') + 3);
                    if (formattedValue !== value) {
                        this.$refs.input.value = formattedValue;
                    }
                    this.$emit('input', Number(formattedValue));
                },
            },
        });
        new Vue({
            el: '#currency-input-example',
            data: {
                price: '',
            },
        })
    </script>

    <!-- 自定义事件 - 自定义组件的 v-model -->
    <!-- https://cn.vuejs.org/v2/guide/components.html#%E8%87%AA%E5%AE%9A%E4%B9%89%E7%BB%84%E4%BB%B6%E7%9A%84-v-model -->

    <!-- 自定义事件 - 非父子组件的通信 -->
    <!-- 在简单的场景下，可以使用一个空的 Vue 实例作为事件总线。 -->
    <script>
        const bus = new Vue();
        bus.$emit('id-selected', 1);
        bus.$on('id-selected', id => {
            // do sth
        });
    </script>
</body>
</html>
